package com.koron.util;

import com.koron.css2.ApplicationConfig;
import com.koron.css2.serviceManage.dto.DataSourceConfigDTO;
import com.koron.css2.serviceManage.mapper.DbServerMapper;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.koron.ebs.mybatis.ADOConnection;
import org.koron.ebs.mybatis.ADOSessionImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 数据源工具类
 * 
 * @author 许海宜 2019年9月26日
 */
@Component
public class DBSourceUtils {

	private static Logger logger = LogManager.getLogger(DBSourceUtils.class);
	
	private static HikariConfig hikariConfig;
	
	@Autowired
	public void setHikariConfig(HikariConfig hikariConfig) {
		DBSourceUtils.hikariConfig = hikariConfig;
	}

	// 用于存储所有注册的数据源，方便后续调用
	private static Map<String, String> dbEnvMap = new ConcurrentHashMap<String, String>();

	public static String getDbEnv(String companyNo) {
		String dbEnv = dbEnvMap.get(companyNo + Constant.MASTER); // 默认主数据库
		//如果数据源未注册，则注册数据源
		if(dbEnv == null) {
			DBSourceUtils.registeDataSource(companyNo);
			dbEnv = dbEnvMap.get(companyNo + Constant.MASTER);
		}
		return dbEnv;
	}
	
	/**
	 * 用于获取数据源
	 * @param companyNo 水司CODE
	 * @return
	 */
	public static String getDbEnv(String companyNo, boolean isMaster) {
		String dbEnv = isMaster ? dbEnvMap.get(companyNo + Constant.MASTER) : dbEnvMap.get(companyNo + Constant.SLAVE);
		//如果数据源未注册，则注册数据源
		if(dbEnv == null) {
			DBSourceUtils.registeDataSource(companyNo);
			dbEnv = isMaster ? dbEnvMap.get(companyNo + Constant.MASTER) : dbEnvMap.get(companyNo + Constant.SLAVE);
		}
		if(dbEnv == null && !isMaster) {  // 假如未注册从数据源，默认取主数据源
			dbEnv = dbEnvMap.get(companyNo + Constant.MASTER);
		}
		return dbEnv;
	}
	
	/**
	 * 注册新数据源
	 * 
	 * @param bean
	 */
	private static void registeDbMap(DataSourceConfigDTO bean) {
		if (bean == null) {
			throw new RuntimeException("未获取到数据源信息！！！");
		}
		
		if(dbEnvMap.get(bean.getCompanyNo() + Constant.MASTER) == null) {
			setDataBaseInfo(hikariConfig, bean.getDbType(), bean.getDbIP(), bean.getDbPort(), bean.getDbSchema());
			hikariConfig.setUsername(bean.getAccount());
			hikariConfig.setPassword(bean.getPassWord());
			HikariDataSource masterDataSource = new HikariDataSource(hikariConfig);
			
			// 添加分页拦截器
			InitParam.session.addInterceptor("_DB" + bean.getCompanyNo() + Constant.MASTER, "com.koron.util.PageInterceptor");
			// 注册新的数据源
			InitParam.session.registeDataSourceMap("_DB" + bean.getCompanyNo() + Constant.MASTER, masterDataSource);
			// 存放数据源
			dbEnvMap.put(bean.getCompanyNo() + Constant.MASTER, "_DB" + bean.getCompanyNo() + Constant.MASTER);
			logger.info("已注册主数据源：_DB"  + bean.getCompanyNo() + Constant.MASTER);
		}
		
		// 判断从数据库配置是否为空
		if(!StringUtils.isAnyBlank(bean.getSlaveAccount(), bean.getSlaveDbIP(), bean.getSlaveDbPort(), bean.getSlavePassWord())) {
			if(dbEnvMap.get(bean.getCompanyNo() + Constant.SLAVE) == null) {
				setDataBaseInfo(hikariConfig, bean.getDbType(), bean.getSlaveDbIP(), bean.getSlaveDbPort(), bean.getDbSchema());
				hikariConfig.setUsername(bean.getSlaveAccount());
				hikariConfig.setPassword(bean.getSlavePassWord());
				HikariDataSource slaveDataSource = new HikariDataSource(hikariConfig);
				
				// 添加分页拦截器
				InitParam.session.addInterceptor("_DB" + bean.getCompanyNo() + Constant.SLAVE, "com.koron.util.PageInterceptor");
				// 注册新的数据源
				InitParam.session.registeDataSourceMap("_DB" + bean.getCompanyNo() + Constant.SLAVE, slaveDataSource);
				// 存放数据源
				dbEnvMap.put(bean.getCompanyNo() + Constant.SLAVE, "_DB" + bean.getCompanyNo() + Constant.SLAVE);
				logger.info("已注册从数据源：_DB"  + bean.getCompanyNo() + Constant.SLAVE);
			}
		}
	}

	private static void setDataBaseInfo(HikariConfig config, String dbType, String dbIP, String dbPort, String dbSchema) {
		String driver, url, testSql;
		// 配置新的数据源
		if (dbType.toUpperCase().equals("ORACLE")) {
			driver = "oracle.jdbc.driver.OracleDriver";
			url = "jdbc:oracle:thin:@(description=(address=(protocol=tcp)(port=" + dbPort + ")(host=" + dbIP + "))(connect_data=(service_name=" + dbSchema + ")))";
			testSql = "select sysdate from dual";
		} else if (dbType.toUpperCase().equals("MYSQL")) {
			driver = "com.mysql.jdbc.Driver";
			url = "jdbc:mysql://" + dbIP + ":" + dbPort + "/"
					+ dbSchema
					+ "?useUnicode=true&AutoReconnect=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
			testSql = "select now()";
		} else {
			logger.debug("没有找到对应的数据库类型！");
			throw new RuntimeException("没有找到对应的数据库类型！");
		}
		config.setDriverClassName(driver);
		config.setJdbcUrl(url);
		config.setConnectionTestQuery(testSql);
	}

	/**
	 * 初始化水司数据源
	 * 
	 * @param sessionFactory
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static void initDataSources() {
		// 获取所有水司数据库配置
		List<DataSourceConfigDTO> list = ADOConnection.runTask("_default", factory -> {
			DbServerMapper mapper = factory.getMapper(DbServerMapper.class);
			return mapper.selectDataSource(ApplicationConfig.getWaterCodeFilter());
		}, List.class);
		// 注册新的数据源
		for (DataSourceConfigDTO bean : list) {
			registeDbMap(bean);
		}
	}

	/**
	 * 注册指定数据源
	 * 
	 * @param sessionFactory
	 * @param mark           数据源别名
	 * @return
	 */
	public synchronized static void registeDataSource(String companyNo) {
		// 获取指定水司数据库配置
		DataSourceConfigDTO dataSourceConfigDTO = ADOConnection.runTask(factory -> {
			DbServerMapper mapper = factory.getMapper(DbServerMapper.class);
			return mapper.selectDataSourceByCompanyNo(companyNo, ApplicationConfig.getWaterCodeFilter());
		}, DataSourceConfigDTO.class);
		// 注册新的数据源
		registeDbMap(dataSourceConfigDTO);
	}

	/**
	 * 移除指定数据库
	 * 
	 * @param sessionFactory
	 * @param mark
	 */
	public static void removeDataSource(String account) {
		new ADOSessionImpl().removeEnvSource(account);
	}

	/**
	 * 获取已注册数据源的水司编号列表
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<String> getCompanyNoList() {
		List<String> companyList = ADOConnection.runTask("_default", factory->{
	        DbServerMapper dbMapper = factory.getMapper(DbServerMapper.class);
	        return dbMapper.selectCompany(ApplicationConfig.getWaterCodeFilter());
		}, List.class);  
		return companyList;
	}
}
